﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HslCommunication;
using HslCommunication.Profinet.Siemens;

namespace HslDemo
{
    /// <summary>
    /// 西门子S7
    /// </summary>
    class S7Client
    {
        public SiemensS7Net siemens_PLCDrive;//西门子
        public OperateResult PlcConnect = null;//PLC连接
        string _DeviceAdress;//PLCIP
        SiemensPLCS SiemensMachineType;//西门子类型


        public bool IsConnend
        {
            get { return PlcConnect != null ? PlcConnect.IsSuccess : false; }
        }

        public S7Client(string SiemensMachine, string MachineIP)
        {
            switch (SiemensMachine)
            {
                case "S7 1200":
                    SiemensMachineType = SiemensPLCS.S1200;
                    break;
                case "S7 300":
                    SiemensMachineType = SiemensPLCS.S300;
                    break;
                case "S7 400":
                    SiemensMachineType = SiemensPLCS.S400;
                    break;
                case "S7 1500":
                    SiemensMachineType = SiemensPLCS.S1500;
                    break;
                case "S7 200Smart":
                    SiemensMachineType = SiemensPLCS.S200Smart;
                    break;
                case "S7 200":
                    SiemensMachineType = SiemensPLCS.S200;
                    break;
                default:
                    break;
            }
            _DeviceAdress = MachineIP;
        }

        #region 开启服务
        public bool Open()
        {
            try
            {
                siemens_PLCDrive = new SiemensS7Net(SiemensMachineType, _DeviceAdress);//与PLC连接
                PlcConnect = siemens_PLCDrive.ConnectServer();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        #endregion

        #region 关闭服务
        public bool Close()
        {
            try
            {
                siemens_PLCDrive.ConnectClose();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
        #endregion

        #region 读
        /// <summary>
        /// 读
        /// </summary>
        /// <param name="IODataType">读取数据类型</param>
        /// <param name="IOAdress">地址</param>
        /// <returns></returns>
        public object S7Read(PLCDataType IODataType, string IOAdress)
        {
            switch (IODataType)
            {
                case PLCDataType.PLCBool:
                    bool res1 = siemens_PLCDrive.ReadBool(IOAdress).Content;
                    return res1;
                case PLCDataType.PLCByte:
                    byte res2 = siemens_PLCDrive.ReadByte(IOAdress).Content;
                    return res2;
                case PLCDataType.PLCInt16:
                    short res3 = siemens_PLCDrive.ReadInt16(IOAdress).Content;
                    return res3;
                case PLCDataType.PLCUInt16:
                    ushort res4 = siemens_PLCDrive.ReadUInt16(IOAdress).Content;
                    return res4;
                case PLCDataType.PLCInt32:
                    int res5 = siemens_PLCDrive.ReadInt32(IOAdress).Content;
                    return res5;
                case PLCDataType.PLCUInt32:
                    uint res6 = siemens_PLCDrive.ReadUInt32(IOAdress).Content;
                    return res6;
                case PLCDataType.PLCFloat:
                    float res7 = siemens_PLCDrive.ReadFloat(IOAdress).Content;
                    return res7;
                case PLCDataType.PLCInt64:
                    long res8 = siemens_PLCDrive.ReadInt64(IOAdress).Content;
                    return res8;
                case PLCDataType.PLCUInt64:
                    ulong res9 = siemens_PLCDrive.ReadUInt64(IOAdress).Content;
                    return res9;
                case PLCDataType.PLCDouble:
                    double res10 = siemens_PLCDrive.ReadDouble(IOAdress).Content;
                    return res10;
                case PLCDataType.PLCString:
                    string res11 = siemens_PLCDrive.ReadString(IOAdress, 10).Content;
                    return res11;
            }
            return null;
        }
        #endregion

        #region 写
        /// <summary>
        /// 写
        /// </summary>
        /// <param name="IODataType">数据类型</param>
        /// <param name="IOAdress">地址</param>
        /// <param name="IOValue">值</param>
        /// <returns></returns>
        public bool S7Write(PLCDataType IODataType, string IOAdress, object IOValue)
        {
            OperateResult res = new OperateResult();
            try
            {
                switch (IODataType)
                {
                    case PLCDataType.PLCBool:
                        res = siemens_PLCDrive.Write(IOAdress, (bool)IOValue);
                        break;
                    case PLCDataType.PLCByte:
                        res = siemens_PLCDrive.Write(IOAdress, (byte)IOValue);
                        break;
                    case PLCDataType.PLCInt16:
                        res = siemens_PLCDrive.Write(IOAdress, (short)IOValue);
                        break;
                    case PLCDataType.PLCUInt16:
                        res = siemens_PLCDrive.Write(IOAdress, (ushort)IOValue);
                        break;
                    case PLCDataType.PLCInt32:
                        res = siemens_PLCDrive.Write(IOAdress, (int)IOValue);
                        break;
                    case PLCDataType.PLCUInt32:
                        res = siemens_PLCDrive.Write(IOAdress, (uint)IOValue);
                        break;
                    case PLCDataType.PLCFloat:
                        res = siemens_PLCDrive.Write(IOAdress, (float)IOValue);
                        break;
                    case PLCDataType.PLCInt64:
                        res = siemens_PLCDrive.Write(IOAdress, (long)IOValue);
                        break;
                    case PLCDataType.PLCUInt64:
                        res = siemens_PLCDrive.Write(IOAdress, (ulong)IOValue);
                        break;
                    case PLCDataType.PLCDouble:
                        res = siemens_PLCDrive.Write(IOAdress, (double)IOValue);
                        break;
                    case PLCDataType.PLCString:
                        res = siemens_PLCDrive.Write(IOAdress, IOValue.ToString());
                        break;
                    default:
                        return false;
                }
                if (res.IsSuccess)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception)
            {
                return false;
                throw;
            }
        }

        #endregion

        #region 读批量
        /// <summary>
        /// 读批量
        /// </summary>
        /// <param name="IODataType">读取数据类型</param>
        /// <param name="IOAdress">地址</param>
        ///  /// <param name="Count">个数</param>
        /// <returns></returns>
        public object[] S7ReadArray(PLCDataType IODataType, string IOAdress, ushort Count)
        {
            switch (IODataType)
            {
                case PLCDataType.PLCBool:
                    bool[] res1 = siemens_PLCDrive.ReadBool(IOAdress, Count).Content;
                    object[] ObjBool = new object[res1.Length];
                    res1.CopyTo(ObjBool, 0);
                    return ObjBool;
                case PLCDataType.PLCInt16:
                    short[] res3 = siemens_PLCDrive.ReadInt16(IOAdress, Count).Content;
                    object[] ObjInt16 = new object[res3.Length];
                    res3.CopyTo(ObjInt16, 0);
                    return ObjInt16;
                case PLCDataType.PLCUInt16:
                    ushort[] res4 = siemens_PLCDrive.ReadUInt16(IOAdress, Count).Content;
                    object[] ObjUInt16 = new object[res4.Length];
                    res4.CopyTo(ObjUInt16, 0);
                    return ObjUInt16;
                case PLCDataType.PLCInt32:
                    int[] res5 = siemens_PLCDrive.ReadInt32(IOAdress, Count).Content;
                    object[] ObjInt32 = new object[res5.Length];
                    res5.CopyTo(ObjInt32, 0);
                    return ObjInt32;
                case PLCDataType.PLCUInt32:
                    uint[] res6 = siemens_PLCDrive.ReadUInt32(IOAdress, Count).Content;
                    object[] ObjUInt32 = new object[res6.Length];
                    res6.CopyTo(ObjUInt32, 0);
                    return ObjUInt32;
                case PLCDataType.PLCFloat:
                    float[] res7 = siemens_PLCDrive.ReadFloat(IOAdress, Count).Content;
                    object[] ObjFloat = new object[res7.Length];
                    res7.CopyTo(ObjFloat, 0);
                    return ObjFloat;
                case PLCDataType.PLCInt64:
                    long[] res8 = siemens_PLCDrive.ReadInt64(IOAdress, Count).Content;
                    object[] ObjInt64 = new object[res8.Length];
                    res8.CopyTo(ObjInt64, 0);
                    return ObjInt64;
                case PLCDataType.PLCUInt64:
                    ulong[] res9 = siemens_PLCDrive.ReadUInt64(IOAdress, Count).Content;
                    object[] ObjUInt64 = new object[res9.Length];
                    res9.CopyTo(ObjUInt64, 0);
                    return ObjUInt64;
                case PLCDataType.PLCDouble:
                    double[] res10 = siemens_PLCDrive.ReadDouble(IOAdress, Count).Content;
                    object[] ObjDouble = new object[res10.Length];
                    res10.CopyTo(ObjDouble, 0);
                    return ObjDouble;
            }
            return null;
        }
        #endregion

        #region 写批量
        /// <summary>
        /// 写批量
        /// </summary>
        /// <param name="IODataType">数据类型</param>
        /// <param name="IOAdress">地址</param>
        /// <param name="IOValue">值</param>
        /// <returns></returns>
        public bool S7WriteArray(PLCDataType IODataType, string IOAdress, object[] IOValue)
        {
            OperateResult res = new OperateResult();
            try
            {
                switch (IODataType)
                {
                    case PLCDataType.PLCBool:
                        bool[] BoolArray = new bool[IOValue.Length];
                        IOValue.CopyTo(BoolArray, 0);
                        res = siemens_PLCDrive.Write(IOAdress, BoolArray);
                        break;
                    case PLCDataType.PLCByte:
                        byte[] ByteArray = new byte[IOValue.Length];
                        IOValue.CopyTo(ByteArray, 0);
                        res = siemens_PLCDrive.Write(IOAdress, ByteArray);
                        break;
                    case PLCDataType.PLCInt16:
                        short[] Int16Array = new short[IOValue.Length];
                        IOValue.CopyTo(Int16Array, 0);
                        res = siemens_PLCDrive.Write(IOAdress, Int16Array);
                        break;
                    case PLCDataType.PLCUInt16:
                        ushort[] UInt16 = new ushort[IOValue.Length];
                        IOValue.CopyTo(UInt16, 0);
                        res = siemens_PLCDrive.Write(IOAdress, UInt16);
                        break;
                    case PLCDataType.PLCInt32:
                        int[] Int32Array = new int[IOValue.Length];
                        IOValue.CopyTo(Int32Array, 0);
                        res = siemens_PLCDrive.Write(IOAdress, Int32Array);
                        break;
                    case PLCDataType.PLCUInt32:
                        uint[] UInt32Array = new uint[IOValue.Length];
                        IOValue.CopyTo(UInt32Array, 0);
                        res = siemens_PLCDrive.Write(IOAdress, UInt32Array);
                        break;
                    case PLCDataType.PLCFloat:
                        float[] FloatArray = new float[IOValue.Length];
                        IOValue.CopyTo(FloatArray, 0);
                        res = siemens_PLCDrive.Write(IOAdress, FloatArray);
                        break;
                    case PLCDataType.PLCInt64:
                        long[] Int64Array = new long[IOValue.Length];
                        IOValue.CopyTo(Int64Array, 0);
                        res = siemens_PLCDrive.Write(IOAdress, Int64Array);
                        break;
                    case PLCDataType.PLCUInt64:
                        ulong[] UInt64Array = new ulong[IOValue.Length];
                        IOValue.CopyTo(UInt64Array, 0);
                        res = siemens_PLCDrive.Write(IOAdress, UInt64Array);
                        break;
                    case PLCDataType.PLCDouble:
                        double[] DoubleArray = new double[IOValue.Length];
                        IOValue.CopyTo(DoubleArray, 0);
                        res = siemens_PLCDrive.Write(IOAdress, DoubleArray);
                        break;
                    default:
                        return false;
                }
                if (res.IsSuccess)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception)
            {
                return false;
                throw;
            }
        }

        #endregion

        #region 读(泛型单一)
        /// <summary>
        /// 读(泛型单一)
        /// </summary>
        /// <param name="IODataType">读取数据类型</param>
        /// <param name="IOAdress">地址</param>
        /// <returns></returns>
        public T S7Read<T>(string IOAdress)
        {
            try
            {
                switch (typeof(T).Name)
                {
                    case "Boolean":
                        object res1 = siemens_PLCDrive.ReadBool(IOAdress).Content;
                        return (T)res1;
                    case "Byte":
                        object res2 = siemens_PLCDrive.ReadByte(IOAdress).Content;
                        return (T)res2;
                    case "Int16":
                        object res3 = siemens_PLCDrive.ReadInt16(IOAdress).Content;
                        return (T)res3;
                    case "UInt16":
                        object res4 = siemens_PLCDrive.ReadUInt16(IOAdress).Content;
                        return (T)res4;
                    case "Int32":
                        object res5 = siemens_PLCDrive.ReadInt32(IOAdress).Content;
                        return (T)res5;
                    case "UInt32":
                        object res6 = siemens_PLCDrive.ReadUInt32(IOAdress).Content;
                        return (T)res6;
                    case "Float":
                        object res7 = siemens_PLCDrive.ReadFloat(IOAdress).Content;
                        return (T)res7;
                    case "Int64":
                        object res8 = siemens_PLCDrive.ReadInt64(IOAdress).Content;
                        return (T)res8;
                    case "UInt64":
                        object res9 = siemens_PLCDrive.ReadUInt64(IOAdress).Content;
                        return (T)res9;
                    case "Double":
                        object res10 = siemens_PLCDrive.ReadDouble(IOAdress).Content;
                        return (T)res10;
                    case "String":
                        object res11 = siemens_PLCDrive.ReadString(IOAdress, 10).Content;
                        return (T)res11;
                    default:
                        return default(T);
                }
            }
            catch (Exception)
            {
                return default(T);
            }
   
            
        }
        #endregion

        #region 读(泛型批量)
        /// <summary>
        /// 读(泛型批量)
        /// </summary>
        /// <param name="IODataType">读取数据类型</param>
        /// <param name="IOAdress">地址</param>
        ///  /// <param name="Count">个数</param>
        /// <returns></returns>
        public List<T> S7ReadArray<T>(string IOAdress, ushort Count)
        {
            try
            {
                switch (typeof(T).Name)
                {
                    case "Boolean":
                        bool[] res1 = siemens_PLCDrive.ReadBool(IOAdress, Count).Content;
                        if (res1 == null)
                        {
                            return null;
                        }
                        object[] ObjBool = new object[res1.Length];
                        res1.CopyTo(ObjBool, 0);
                        return ConvertList<List<T>>(ObjBool);
                    case "Int16":
                        short[] res3 = siemens_PLCDrive.ReadInt16(IOAdress, Count).Content;
                        if (res3 == null)
                        {
                            return null;
                        }
                        object[] ObjInt16 = new object[res3.Length];
                        res3.CopyTo(ObjInt16, 0);
                        return ConvertList<List<T>>(ObjInt16);
                    case "UInt16":
                        ushort[] res4 = siemens_PLCDrive.ReadUInt16(IOAdress, Count).Content;
                        if (res4 == null)
                        {
                            return null;
                        }
                        object[] ObjUInt16 = new object[res4.Length];
                        res4.CopyTo(ObjUInt16, 0);
                        return ConvertList<List<T>>(ObjUInt16);
                    case "Int32":
                        int[] res5 = siemens_PLCDrive.ReadInt32(IOAdress, Count).Content;
                        if (res5 == null)
                        {
                            return null;
                        }
                        object[] ObjInt32 = new object[res5.Length];
                        res5.CopyTo(ObjInt32, 0);
                        return ConvertList<List<T>>(ObjInt32);
                    case "UInt32":
                        uint[] res6 = siemens_PLCDrive.ReadUInt32(IOAdress, Count).Content;
                        if (res6 == null)
                        {
                            return null;
                        }
                        object[] ObjUInt32 = new object[res6.Length];
                        res6.CopyTo(ObjUInt32, 0);
                        return ConvertList<List<T>>(ObjUInt32);
                    case "Single":
                        float[] res7 = siemens_PLCDrive.ReadFloat(IOAdress, Count).Content;
                        if (res7 == null)
                        {
                            return null;
                        }
                        object[] ObjFloat = new object[res7.Length];
                        res7.CopyTo(ObjFloat, 0);
                        return ConvertList<List<T>>(ObjFloat);
                    case "Int64":
                        long[] res8 = siemens_PLCDrive.ReadInt64(IOAdress, Count).Content;
                        if (res8 == null)
                        {
                            return null;
                        }
                        object[] ObjInt64 = new object[res8.Length];
                        res8.CopyTo(ObjInt64, 0);
                        return ConvertList<List<T>>(ObjInt64);
                    case "UInt64":
                        ulong[] res9 = siemens_PLCDrive.ReadUInt64(IOAdress, Count).Content;
                        if (res9 == null)
                        {
                            return null;
                        }
                        object[] ObjUInt64 = new object[res9.Length];
                        res9.CopyTo(ObjUInt64, 0);
                        return ConvertList<List<T>>(ObjUInt64);
                    case "Double":
                        double[] res10 = siemens_PLCDrive.ReadDouble(IOAdress, Count).Content;
                        if (res10 == null)
                        {
                            return null;
                        }
                        object[] ObjDouble = new object[res10.Length];
                        res10.CopyTo(ObjDouble, 0);
                        return ConvertList<List<T>>(ObjDouble);
                    default:
                        return default;
                }
            }
            catch (Exception)
            {
                return default;
                throw;
            }
        }
        #endregion

        #region 写(泛型单一与批量)
        /// <summary>
        /// 写(泛型单一与批量)
        /// </summary>
        /// <param name="IODataType">数据类型</param>
        /// <param name="IOAdress">地址</param>
        /// <param name="IOValue">值</param>
        /// <returns></returns>
        public bool S7Write<T>(string IOAdress, T IOValue)
        {
            OperateResult res = new OperateResult();
            try
            {
                switch (typeof(T).Name)
                {
                    case "Boolean":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToBoolean(IOValue));
                        break;
                    case "Byte":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToByte(IOValue));
                        break;
                    case "Int16":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToInt16(IOValue));
                        break;
                    case "UInt16":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToUInt16(IOValue));
                        break;
                    case "Int32":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToInt32(IOValue));
                        break;
                    case "UInt32":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToUInt32(IOValue));
                        break;
                    case "Single":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToSingle(IOValue));
                        break;
                    case "Int64":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToInt64(IOValue));
                        break;
                    case "UInt64":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToUInt64(IOValue));
                        break;
                    case "Double":
                        res = siemens_PLCDrive.Write(IOAdress, Convert.ToDouble(IOValue));
                        break;
                    case "String":
                        res = siemens_PLCDrive.Write(IOAdress, IOValue.ToString());
                        break;
                    case "Boolean[]":
                        bool[] BoolArray = (bool[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, BoolArray);
                        break;
                    case "Byte[]":
                        byte[] ByteArray = (byte[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, ByteArray);
                        break;
                    case "Int16[]":
                        short[] Int16Array = (short[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, Int16Array);
                        break;
                    case "UInt16[]":
                        ushort[] UInt16 = (ushort[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, UInt16);
                        break;
                    case "Int32[]":
                        int[] Int32Array = (int[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, Int32Array);
                        break;
                    case "UInt32[]":
                        uint[] UInt32Array = (uint[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, UInt32Array);
                        break;
                    case "Single[]":
                        float[] FloatArray = (float[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, FloatArray);
                        break;
                    case "Int64[]":
                        long[] Int64Array = (long[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, Int64Array);
                        break;
                    case "UInt64[]":
                        ulong[] UInt64Array = (ulong[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, UInt64Array);
                        break;
                    case "Double[]":
                        double[] DoubleArray = (double[])(IOValue as Array);
                        res = siemens_PLCDrive.Write(IOAdress, DoubleArray);
                        break;
                    default:
                        return false;
                }
                if (res.IsSuccess)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception)
            {
                return false;
                throw;
            }
        }

        #endregion

        #region object数组转泛型集合
        //object数组转泛型集合
        public T ConvertList<T>(object obj)
        {
            if (obj is object[] )
            {
                object[] objects= (object[])obj;
                // Here we create an instance of type T, where T is actually a generic
                // List<TElement>. Then we cast it to type IList. Therefore here we don't 
                // have to know and specify the type of the elements of List<TElement>.
                IList list = (IList)Activator.CreateInstance(typeof(T));

                foreach (object o in objects)
                    // Here we call IList.Add(object o). Internally it casts added
                    // elements to the actual type of the elements of List<TElement>.
                    list.Add(o);

                return (T)list;
            }
            return default;
        }
        #endregion
    }

    /// <summary>
    /// plc数据类型
    /// </summary>
    public enum PLCDataType//plc数据类型
    {
        PLCBool,
        PLCByte,
        PLCInt16,
        PLCUInt16,
        PLCInt32,
        PLCUInt32,
        PLCFloat,
        PLCInt64,
        PLCUInt64,
        PLCDouble,
        PLCString
    }
}
